//-----------------------------------------------------------------------------
// mapViewer.cpp
//-----------------------------------------------------------------------------

#include "AReVi/arClass.h"
#include "AReVi/arSystem.h"

#include "AReVi/Lib3D/viewer3D.h"
#include "AReVi/Lib3D/base3D.h"
#include "AReVi/Lib3D/boundingBox3D.h"
#include "AReVi/Lib3D/scene3D.h"
#include "AReVi/Lib3D/object3D.h"
#include "AReVi/Lib3D/textureRenderer3D.h"
#include "AReVi/Lib3D/renderTexture.h"
#include "AReVi/Lib3D/urlTexture.h"
#include "AReVi/Lib3D/renderer3D.h"
#include "AReVi/Lib3D/dataTexture.h"
#include "AReVi/Lib3D/osdSprite.h"

using namespace AReVi;

//#include "rigidOsdSprite.h"
//#include "rigidOsdText.h"
#include "aReViRoadViewer.h"
#include "mapViewer.h"
#include "simulator.h"
#include "car.h"
#include "flickers.h"
#include "light.h"
#include "wheels.h"
#include "mirror.h"
#include "pedestrian.h"

#include "Sensor/sensor.h"
#include "Sensor/fovArea.h"

int MapMarker::_nbInstance = 0;

//-----------------------------------------------------------------------------
// Class MapMarker
//-----------------------------------------------------------------------------

AR_CLASS_NOVOID_DEF(MapMarker, Base3D)

//-----------------------------------------------------------------------------

MapMarker::MapMarker(ArCW & arCW, StlString name, StlString type):
Base3D(arCW),
_messageService(MessageService::NEW()),
_act(Activity::NEW(0.1)),
_motionFlag(false),
_name(name),
_userType(type)
{
  _id = _nbInstance++;  

  addMotionCB(thisRef(),&MapMarker::_onMotionCB);
  
  _motionFlag = true;
  
  ArRef<MapMarkerMessage> message = MapMarkerMessage::NEW();
  message->setAction("newItem");
  message->setUserName(_name);
  message->setUserType(_userType);
  message->setID(_id);
  message->setBase(getBase());
//  _messageService->broadcast(message);
  _messageService->sendTo(Simulator::getMapViewer(),message);

  _act->setBehavior(thisRef(),&MapMarker::_activate);
}

MapMarker::~MapMarker()
{
  ArRef<MapMarkerMessage> message = MapMarkerMessage::NEW();
  message->setAction("deleteItem");
  message->setUserName(_name);
  message->setUserType(_userType);
  message->setID(_id);
  
  //_messageService->broadcast(message);
  if(Simulator::getMapViewer().valid()){
    _messageService->sendTo(Simulator::getMapViewer(),message);  
  }
}

//-----------------------------------------------------------------------------

StlString
MapMarker::getName(void){
  return _name;
}

//-----------------------------------------------------------------------------

StlString
MapMarker::getUserType(void){
  return _userType;
}

//-----------------------------------------------------------------------------

int
MapMarker::getID(void){
  return _id;
}

//-----------------------------------------------------------------------------

bool
MapMarker::_activate(ArRef<Activity> /*act*/ , double /*dt*/) {

  if(_motionFlag) {
    ArRef<MapMarkerMessage> message = MapMarkerMessage::NEW();
    message->setAction("moveItem");   
    message->setUserName(_name);
    message->setUserType(_userType);
    message->setID(_id);
    message->setBase(getBase());
    
    _messageService->sendTo(Simulator::getMapViewer(),message);
    //_messageService->broadcast(message);  
    
    _motionFlag=false;
  }

  return true;
}

//-----------------------------------------------------------------------------

void
MapMarker::_onMotionCB(const Base3D::MotionEvent& ){
  _motionFlag = true;
}

//-----------------------------------------------------------------------------
// Class MapMarkerMessage
//-----------------------------------------------------------------------------

AR_CLASS_DEF(MapMarkerMessage, Message)

//-----------------------------------------------------------------------------

MapMarkerMessage::MapMarkerMessage(ArCW & arCW):
Message(arCW),
_base(true)
{
}

MapMarkerMessage::~MapMarkerMessage()
{
}

//-----------------------------------------------------------------------------
// Class MapViewer
//-----------------------------------------------------------------------------

AR_CLASS_NOVOID_DEF(MapViewer, ArObject)

//-----------------------------------------------------------------------------

MapViewer::MapViewer(ArCW & arCW, ArRef<Scene3D> scene):
ArObject(arCW),
_messageService(MessageService::NEW())
{
  addMessageCB(thisRef(),&MapViewer::_onMessageCB); 

  ArRef<TextureRenderer3D> renderer = TextureRenderer3D::NEW();  
  
  renderer->setFarDistance(10000.0);
  renderer->setNearDistance(1.0);
  renderer->setRenderSize(1024,1024);

  ArRef<Scene3D> sceneCopy = Scene3D::NEW();
  
  for(unsigned int i=0;i<scene->getNbObjects();i++){
    if( scene->getObject(i)->getClass()  != Car::CLASS() &&
        scene->getObject(i)->getClass() != Flickers::CLASS() &&
        scene->getObject(i)->getClass() != Mirror::CLASS() &&
        scene->getObject(i)->getClass() != Light::CLASS() &&
        scene->getObject(i)->getClass() != Wheels::CLASS() &&
        scene->getObject(i)->getClass() != Pedestrian::CLASS() &&
        scene->getObject(i)->getClass() != FovArea::CLASS() &&
        !scene->getObject(i)->getClass()->isA(Sensor::CLASS())
      )
      //      cerr <<scene->accessObject(i)->getAlias()  << endl;
    sceneCopy->addObject(scene->accessObject(i));
  }  
  
  renderer->selectScene(sceneCopy);
  
  ArRef<BoundingBox3D> bbox=BoundingBox3D::NEW();
  sceneCopy->computeBoundingBox(bbox,Base3D::nullRef());
    
  double cx = 0.0;
  double cy = 0.0;
  double cz = 0.0;
  double sizeX = 0.0;
  double sizeY = 0.0;
  double sizeZ,d;  
        
  if(bbox->isValid()){
    bbox->getCenter(cx,cy,cz);
    renderer->setPosition(cx,cy,0.0);
    renderer->pitch(M_PI_2);    
    bbox->getSize(sizeX,sizeY,sizeZ);
  }
        
  if(sizeX>sizeY){
    d = sizeX /(2*tan(renderer->getLeftSlope()-0.05));
    _xmin = cx - sizeX/2.0;
    _ymin = cy - sizeX/2.0;
    _xmax = cx + sizeX/2.0;
    _ymax = cy + sizeX/2.0;   
  }
  else{
    d = sizeY / (2*tan(renderer->getUpSlope()-0.05));
    _xmin = cx - sizeY/2.0;
    _ymin = cy - sizeY/2.0;
    _xmax = cx + sizeY/2.0;
    _ymax = cy + sizeY/2.0;    
  }
              
  renderer->translate(-d,0.0,0.0);
  ArRef<RenderTexture> renderTexture = renderer->buildTexture(); 
  renderer->render();    
  
  ArRef<MemoryBlock> dataOut = MemoryBlock::NEW();
  unsigned int  widthOut;
  unsigned int  heightOut;
  unsigned int  componentsOut;
      
  renderer->takeSnapshot(widthOut,heightOut,componentsOut,dataOut);
  ArRef<Texture> dataTexture = DataTexture::NEW(1024,1024,3,dataOut,true,true);
  
  unsigned int sW,sH;
  Renderer3D::getScreenSize(sW,sH);

  _road = OSDSprite::NEW(dataTexture);
  _road->setMotionInteraction(false);
  _road->setMouseButtonInteraction(false);
  _road->setKeyboardInteraction(false);  
  _road->resizeWithRenderer(false);
  _road->setLocation(OSD::LOC_FREE);
  _road->setPosition((int)(sH*2/3.0-sH/2.0),0.0);
  _road->setSize(sH,sH);
  _road->setDepth(-0.5);
  _road->setRatio(1.0);    

  _road->setAlpha(0.75);

  _road->getSize(Renderer3D::nullRef(),_sizeX,_sizeY);
}

MapViewer::~MapViewer()
{
}

//-----------------------------------------------------------------------------
    
void
MapViewer::show(ArRef<Viewer3D> viewer) {
  if(viewer->getClass()==AReViRoadViewer::CLASS()){
    ArRef<AReViRoadViewer> aReViRoadViewer;     
    aReViRoadViewer = ar_down_cast<AReViRoadViewer>(viewer);
    
    aReViRoadViewer->addOSD(_road);
  
    if(aReViRoadViewer->getMapMarker()){
      if(aReViRoadViewer->getMapMarker()->getUserType()=="--observer"){
      }
      else if(aReViRoadViewer->getMapMarker()->getUserType()=="--learner"){
        aReViRoadViewer->addOSD(_items[aReViRoadViewer->getMapMarker()->getID()]);
      }
      else if(aReViRoadViewer->getMapMarker()->getUserType()=="--teacher"){
        StlMap<int,ArRef<OSDSprite> >::iterator itItems = _items.begin();
        
        while(itItems!=_items.end()) {
          aReViRoadViewer->addOSD(itItems->second);
          itItems++;
        }
        
        StlMap<int,ArRef<OSDText> >::iterator itNames = _names.begin();

        while(itNames!=_names.end()) {
          aReViRoadViewer->addOSD(itNames->second);
          itNames++;
        }        
              
      }
      else if(aReViRoadViewer->getMapMarker()->getUserType()=="--god"){
        StlMap<int,ArRef<OSDSprite> >::iterator itItems = _items.begin();
        
        while(itItems!=_items.end()) {
          aReViRoadViewer->addOSD(itItems->second);
          itItems++;
        }
      
        StlMap<int,ArRef<OSDText> >::iterator itNames = _names.begin();

        while(itNames!=_names.end()) {
          aReViRoadViewer->addOSD(itNames->second);
          itNames++;
        }      
      }
    }
    
    StlVector<ArRef<AReViRoadViewer> >::iterator itViewers = _viewers.begin();
    bool in=false;
    
    while(itViewers!=_viewers.end()){
      if(*itViewers==aReViRoadViewer) in =true;
      itViewers++;
    }    
    
    if(!in)_viewers.push_back(aReViRoadViewer);
  }
}

//-----------------------------------------------------------------------------

void
MapViewer::hide(ArRef<Viewer3D> viewer) {
  viewer->removeOSD(_road);

  StlMap<int,ArRef<OSDSprite> >::iterator itItems = _items.begin();
  
  while(itItems!=_items.end()) {
    viewer->removeOSD(itItems->second);
    itItems++;
  }

  StlMap<int,ArRef<OSDText> >::iterator itNames = _names.begin();

  while(itNames!=_names.end()) {
    viewer->removeOSD(itNames->second);
    itNames++;
  }  
  
  StlVector<ArRef<AReViRoadViewer> >::iterator itViewers = _viewers.begin();
  
  while(itViewers!=_viewers.end()) {
    if(*itViewers==viewer) _viewers.erase(itViewers--);
    itViewers++;
  }
}

//-----------------------------------------------------------------------------

void
MapViewer::update(void){
  StlVector<ArRef<AReViRoadViewer> >::iterator itViewers = _viewers.begin();
  
  while(itViewers!=_viewers.end()){
    if((*itViewers).valid()) show(*itViewers);
    else _viewers.erase(itViewers--);
    itViewers++;
  }
}

//-----------------------------------------------------------------------------

bool
MapViewer::isShown(ArRef<Viewer3D> viewer) {
  return viewer->hasOSD(_road);
}
    
//-----------------------------------------------------------------------------

void
MapViewer::_onMessageCB(const MessageEvent & /*evt*/) {

  ArConstRef<Message> message = getNextMessage();
  
  while(message){
  
    if(message->getClass()==MapMarkerMessage::CLASS()){
    
      ArConstRef<MapMarkerMessage> mapMarkerMessage = ar_down_cast<MapMarkerMessage>(message);
    
      int id = mapMarkerMessage->getID();
      StlString action = mapMarkerMessage->getAction();
      StlString name = mapMarkerMessage->getUserName();
            
      if(action == "newItem"){
        unsigned int w = 20;
        unsigned int h = 20;
        
        _items[id] = OSDSprite::NEW(URLTexture::NEW("../img/learnerIcon2.tga",true,true));
        _items[id]->setMotionInteraction(false);
        _items[id]->setMouseButtonInteraction(false);
        _items[id]->setKeyboardInteraction(false); 
        _items[id]->resizeWithRenderer(false);
        _items[id]->setLocation(OSD::LOC_FREE);
        _items[id]->setSize(w,h);
        _items[id]->setDepth(0.5+id/1000.0);
        _items[id]->setRatio(1.0);
        _items[id]->setPosition(0,0);        
        _items[id]->attachTo(_road);
        
        _names[id] = OSDText::NEW();
        _names[id]->setMotionInteraction(false);
        _names[id]->setMouseButtonInteraction(false);
        _names[id]->setKeyboardInteraction(false);        
        _names[id]->setText(name);
        _names[id]->setColor(1.0,0.0,0.0,1.0);
        _names[id]->setDepth(0.75+id/1000.0);
        _names[id]->setLocation(OSD::LOC_FREE);
        _items[id]->setPosition(0,-(h/2 + 2));        
        _names[id]->attachTo(_items[id],OSD::ATTACH_TRANSLATION);

	_items[id]->setAlpha(0.75);
	_names[id]->setAlpha(0.75);
        
        update();
      }
      else if(action == "deleteItem"){
       StlVector<ArRef<AReViRoadViewer> >::iterator itViewers = _viewers.begin();
  
       while(itViewers!=_viewers.end()){
         if((*itViewers).valid()){
           (*itViewers)->removeOSD(_items[id]);
           (*itViewers)->removeOSD(_names[id]);
         }
         itViewers++;
       }
       
        _items.erase(id);
        _names.erase(id);        
        
        update();
      }        
    
      if(action == "newItem" || action == "moveItem"){
        unsigned int width,height;
//        unsigned int  wT,hT;
        double x,y,z,roll,pitch,yaw;
        mapMarkerMessage->getBase().getPosition(x,y,z);
        mapMarkerMessage->getBase().extractOrientation(roll,pitch,yaw);
        
        bool out;
        
        if(x>_xmax) {x=_xmax; out = true;}
        if(x<_xmin) {x=_xmin; out = true;}
        if(y>_ymax) {y=_ymax; out = true;}
        if(y<_ymin) {y=_ymin; out = true;}
        
        _items[id]->getSize(Renderer3D::nullRef(),width,height);

        
        int xoffset,yoffset;
        _road->getPosition(Renderer3D::nullRef(),xoffset,yoffset);
        
        _items[id]->setPosition((int)(((_sizeX/(_ymin-_ymax))*(y-_ymax))-(width/2.0)+xoffset),
                                (int)(((_sizeY/(_xmin-_xmax))*(x-_xmax))-(height/2.0))+yoffset);
        _items[id]->setOrientation(yaw);        
        //_names[id]->getBoundingBox(wT,hT);
//         wT = 0;
//         hT = 0;
        
/*        int px = (int)(((_sizeX/(_ymin-_ymax))*(y-_ymax))-(wT/2.0));
        int py = (int)(((_sizeY/(_xmin-_xmax))*(x-_xmax)));
        
        if(px<0) px=0;  
        if(py<0) py=0;*/      
        
/*        _names[id]->setPosition((int)(((_sizeX/(_ymin-_ymax))*(y-_ymax))-(wT/2.0))+xoffset,
                                (int)(((_sizeY/(_xmin-_xmax))*(x-_xmax)))+yoffset);*/
                  
//           if(ar_down_cast<URLTexture>(_items[id]->getTexture())->getURL()!="../img/cross.tga"){
//             _items[id] = RigidOSDSprite::NEW(URLTexture::NEW("../img/cross.tga",true,true));
//           }
      }
    }
    message = getNextMessage();
  }
}
 
//-----------------------------------------------------------------------------


